//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Windows.Documents;

namespace OpenLS.Spreadsheet
{
    public abstract class FullRange : SimpleRange
    {
        protected FullRange(Worksheet worksheet)
        {
            worksheet.AddWeakRangeChanging(OnRangeChanging);
        }

        private void OnRangeChanging(object sender, RangeChangeEventArgs e)
        {
            if (sender == this)
                return;
            if (e.Type == RangeChangeType.Deletion)
                OnDeletingRange(e);
        }

        internal abstract void OnDeletingRange(RangeChangeEventArgs e);

        internal override bool IsProtected
        {
            get { return Worksheet.Protection != null; }
        }

        internal override bool IsSingleCell
        {
            get { return false; }
        }

        internal abstract Dimension FirstDimension { get; }
        internal abstract Dimension LastDimension { get; }

        public override string Label
        {
            get { return FirstDimension.Label + ":" + LastDimension.Label; }
        }


        public override sealed Worksheet Worksheet
        {
            get { return FirstDimension.Worksheet; }
        }

        internal override object GetFirstCellValue(CellProperty p)
        {
            return FirstDimension.GetCellValue(p);
        }

        internal override IEnumerable<CellFormattingRecord> GetFormattingRecords()
        {
            foreach (Dimension dim in GetDimensions())
                yield return dim.FormattingRecord;
            foreach (Cell cell in GetNonEmptyCells())
                yield return cell.FormattingRecord;
        }

        internal override IEnumerable<SimpleRange> GetSimpleRanges()
        {
            yield return this;
        }

        internal override void CopyFromImp(CellAddress sourceTopLeft, IEnumerable<SimpleRange> sources,
                                           PasteInfo pasteInfo)
        {
            RangeFunction fn = pasteInfo.Functions;
//if (source.GetType() != GetType())
            //              throw new NotSupportedException();
            if (fn.ClearDimension != null)
                foreach (Dimension dim in GetDimensions())
                    fn.ClearDimension(dim);
            foreach (Cell cell in GetNonEmptyCells())
                fn.ClearCell(cell);
            CellRelativeAddress offset = Address.TopLeft - sourceTopLeft;
            foreach (SimpleRange source in sources)
            {
                foreach (Cell cell in source.GetNonEmptyCells())
                    fn.CopyFunction(cell, Worksheet[cell.Address + offset], pasteInfo);
                if (source is FullRange)
                    CopyFullFrom(sourceTopLeft, (FullRange) source, fn);
            }
        }

        private void CopyFullFrom(CellAddress sourceTopLeft, FullRange fullRange, RangeFunction fn)
        {
            var d = new List<Dimension>(GetDimensions());
            var s = new List<Dimension>(fullRange.GetDimensions());
            for (int i = 0; i < Math.Min(d.Count, s.Count); i++)
                fn.CopyDimensionFunction(s[i], d[i]);
        }

        internal override CellFormattingRecord GetFormattingRecord()
        {
            return FirstDimension.FormattingRecord;
        }

        internal override sealed FlowDocument ToFlowDocument(string columnSeparator)
        {
            MultiCellsRange r = ToMultiCellsRange();
            return r.ToFlowDocument(columnSeparator);
        }

        private MultiCellsRange ToMultiCellsRange()
        {
            int minRow = FirstCell.Row.Index;
            int maxRow = minRow;
            int minColumn = FirstCell.Column.Index;
            int maxColumn = minColumn;
            foreach (Cell c in GetNonEmptyCells())
            {
                minRow = Math.Min(minRow, c.Row.Index);
                maxRow = Math.Max(maxRow, c.Row.Index);
                minColumn = Math.Min(minColumn, c.Column.Index);
                maxColumn = Math.Max(maxColumn, c.Column.Index);
            }
            return new MultiCellsRange(Worksheet[minRow, minColumn], Worksheet[maxRow, maxColumn]);
        }

        internal override object GetCellValue(CellProperty p, out bool uniform)
        {
            Dimension d = FirstDimension;
            object o = d.GetCellValue(p);
            Contract.Assert(!(o is Exception));
            foreach (Cell c in GetNonEmptyCells())
            {
                object oo = c.GetCellValue(p);
                if (!Equals(oo, o))
                {
                    uniform = false;
                    return o;
                }
            }
            uniform = true;
            return o;
        }

        internal void SetCellValueNoUndoForSelf(CellProperty p, object value)
        {
            foreach (Cell c in GetNonEmptyCells())
                c.SetCellValue(p, value);
            foreach (Dimension d in GetDimensions())
                d.SetCellValue(p, value);
        }

        internal override void SetStyleNoUndo(CellStyle s)
        {
            foreach (Cell c in GetNonEmptyCells())
                c.Style = (s);
            foreach (Dimension dim in GetDimensions())
                dim.Style = (s);
        }

        internal override void SetStyleRecordNoUndo(CellFormattingRecord styleRecord)
        {
            base.SetStyleRecordNoUndo(styleRecord);
            foreach (Dimension dim in GetDimensions())
            {
                dim.SetStyleRecordSafe(styleRecord);
            }
        }


        internal abstract IEnumerable<Dimension> GetDimensions();

        internal override IEnumerable<Cell> GetNonEmptyCells()
        {
            if (Worksheet.NonVolatileCells.Count < LastDimension.Index - FirstDimension.Index)
            {
                foreach (Cell cell in Worksheet.NonVolatileCells)
                {
                    if (Contains(cell))
                        yield return cell;
                }
                yield break;
            }
            foreach (Dimension d in GetDimensions())
                foreach (Cell c in d.GetNonEmptyCells())
                    yield return c;
        }

        internal void CopyDimensions(FullRange source)
        {
            var sourceDims = new List<Dimension>(source.GetDimensions());
            var destinationDims = new List<Dimension>(GetDimensions());
            for (int i = 0; i < Math.Min(sourceDims.Count, destinationDims.Count); i++)
            {
                CellFormattingRecord r1 = sourceDims[i].FormattingRecord;
                sourceDims[i].ClearStyleRecord();
                destinationDims[i].SetStyleRecordSafe(r1);
            }
        }

        internal override sealed Border GetBorder(BorderType borderType, out bool uniform)
        {
            List<Border> borders = Enumerable.ToList(Enumerable.Distinct(GetBorders(borderType)));
            switch (borders.Count)
            {
                case 0:
                    uniform = true;
                    return null;
                case 1:
                    uniform = true;
                    return borders[0];
                default:
                    uniform = false;
                    return null;
            }
        }

        internal abstract IEnumerable<Border> GetBorders(BorderType borderType);
    }
}